#ifndef __LIOFLAT_H_
#define __LIOFLAT_H_

#include "lio3d.h"

namespace Lio {

enum FlatActions {
	FLAT_ACTION_NONE,
	FLAT_ACTION_DEFAULT,
	FLAT_ACTION_EDIT,
	FLAT_ACTION_SELECT,
	FLAT_ACTION_DELETE,
	FLAT_ACTION_SHOW};

#define PLOT_HEADER '\0TLP'
#define CHNL_HEADER 'LNHC'

class FlatComponent: public Caller, public Listener
{
private:
	void over(FlatComponent* child);
protected:
	typedef ListObject<FlatComponent*>* FlatObject;
	FlatComponent *last,*activated,*overed,*parent;
	int minZIndex,maxZIndex,tabIndex,cIndex,zIndex;
	HCURSOR cursor;
	BOOL overState,activeState;
	Rect<float> rect;
	virtual void setOver(BOOL overed){overState=overed;}
	virtual void setActive(BOOL active){activeState=active;}
	virtual BOOL resize(){return FALSE;}
	virtual void locate();
	BOOL call(WORD code, const void *info = NULL)
	{
		return Caller::call(parent,code,info);
	}
public:
	BOOL alignx,aligny;
	Window *window;
	Font *commonFont;
	List<FlatComponent *> childs;
	Rect<float> bounds;
	Point2d<float> margin,stickMargin,location,size;
	FlatComponent *sticker;
	BOOL
		visible,
		disabled,
		adjustSize,
		isStatic;
	float
		borderLine,
		activeLine;
	DWORD 
		borderColor,
		disabledColor,
		bgColor,
		fontColor;
	int adjustLocation;
	_TCHAR symbol;
	BYTE code;
	Font *font;
	FlatComponent();
	BOOL activate(FlatComponent *child);
	BOOL addChild(FlatComponent *child);
	BOOL addChild(FlatComponent *child, int dir, float x = 0, float y = 0,  FlatComponent *reference = NULL)
	{
		child->stick(dir,x,y,reference);
		return addChild(child);
	}
	void remChilds();
	BOOL stick(int dir = 0, float x = 0, float y = 0,  FlatComponent *comp = NULL);
	virtual void action(){call(FLAT_ACTION_DEFAULT);}
	FlatComponent* getChildByCoor(float x, float y, BOOL onlyVisible = TRUE);
	FlatComponent* getChildByID(int id);
	FlatComponent* getChildBySymbol(_TCHAR symbol);
	FlatComponent* getChildByCode(BYTE code);
	FlatComponent* getLastChild() const {return last;}
	FlatComponent* getAciveChild() const {return activated;}
	FlatComponent* getOverChild() const {return overed;}
	virtual BOOL listen(WORD id, WORD code, const void *info)
	{
		return window?PostMessage(window->hWnd,WM_USER_CALL,(WPARAM(code)<<16)|id,(LPARAM)info):FALSE;
	}
	virtual BOOL input(BYTE type,UINT msg,const Mouse &mouse,const Keyboard &keyboard);
	virtual void build();
	virtual void draw();
	BOOL setCursor(CSTR cursorName)
	{
		HCURSOR tmp=LoadCursor(NULL,cursorName);
		if(!tmp)
			return FALSE;
		cursor=tmp;
		return TRUE;
	}
	virtual HCURSOR getCursor()
	{
		if(overed)
			return NULL;
		return cursor;
	}
	void setBounds(float left, float top, float width, float height)
	{
		location.set(left,top);
		size.set(width,height);
	}
	int getZIndex() const {return zIndex;}
	int getCIndex() const {return cIndex;}
	const Rect<float>& getRect()const{return rect;}
	virtual void copy(const FlatComponent &equals);
	BOOL isActive() const {return activeState;}
	BOOL isOver() const {return overState;}
	int getMaxZIndex() const {return maxZIndex;}
	int getMinZIndex() const {return minZIndex;}
};
class FlatScroll:public FlatComponent
{
protected:
	virtual void draw();
	double ratio;
	int direction;
public:
	double
		step,
		page;
	int colorStart,
		colorMiddle,
		colorEnd;
	FlatScroll();
	double setRatio(double newRatio, BOOL event = TRUE);
	double getRatio() const {return ratio;}
	void setDirection(int dir);
	int getDirection() const {return direction;}
	double increase(BOOL event = TRUE)
	{
		return setRatio(ratio+step,event);
	}
	double decrease(BOOL event = TRUE)
	{
		return setRatio(ratio-step,event);
	}
	virtual BOOL input(BYTE type,UINT msg,const Mouse &mouse,const Keyboard &keyboard);
	virtual void copy(const FlatScroll &scroll);
};

class FlatLabel:public FlatComponent
{
public:
	String text;
	FlatLabel(){isStatic=TRUE;}
	virtual BOOL resize();
	void draw();
};

class FlatButton:public FlatLabel
{
protected:
	virtual void draw();
	virtual void setActive(BOOL active)
	{
		FlatComponent::setActive(active);
		if(!checkable&&!active)
			checked=FALSE;
	}
public:
	int 
		overColor,
		pressColor;
	BOOL checkable,checked;
	FlatButton();
	virtual void action();
	virtual BOOL input(BYTE type,UINT msg,const Mouse &mouse,const Keyboard &keyboard);
	virtual void copy(const FlatButton &button);
};

class FlatText:public FlatComponent
{
protected:
	virtual void draw();
	int currentIndex,selectStartI;
	float currentCoorX,slideCoorX,selectLeft,selectWidth;
	float getIndexCoor(int index)
	{
		return font->getTextWidth(text,0,limit(index,0,text.length()));
	}
	void updateCarriageCoor(BOOL clearSelect = TRUE);
	void updateSelection(BOOL clear = FALSE);
	BOOL clearSelected();
	BOOL select;
	BOOL waitChar;
	void selectAll()
	{
		selectStartI=0;
		currentIndex=text.length();
		updateCarriageCoor(FALSE);
	}
public:
	float slide;
	int
		carriageColor,
		selectColor,
		tabSize;
	String text;
	FlatText();
	NumberFormat format;

	int selectLength() const {return absval(currentIndex-selectStartI);}
	virtual BOOL input(BYTE type,UINT msg,const Mouse &mouse,const Keyboard &keyboard);
	void setCarriageCoor(float x, BOOL clearSelect = FALSE);
	void setText(CSTR newText = NULL, BOOL callback = FALSE);
	void setDouble(double value = 0.0, BOOL callback = FALSE)
	{
		setText(format.toString(value),callback);
	}
	void setInt(int value = 0, BOOL callback = FALSE)
	{
		setText(format.toString(value),callback);
	}
	double getDouble(double defValue = 0.0){return format.toDouble(text,defValue);}
	int getInt(int defValue = 0){return format.toInt(text,defValue);}
	CSTR getText()const{return (CSTR)text;}
	void applyTextChages(BOOL callback = FALSE);
	void paste();
	void copy(BOOL cut = FALSE);
	virtual void copy(const FlatText &textBox);
	virtual BOOL resize();
};

class FlatList:public FlatComponent
{
protected:
	int selectIndex;
	List<String> options;
public:
	typedef ListObject<String>* OptionObject;
	int selectBgColor,selectForColor;
	CSTR getSelectedOption()
	{
		OptionObject option=options.objectAt(selectIndex);
		return option?option->value:NULL;
	}
	int getSelectedIndex() const {return selectIndex;}
	int setSelectedIndex(int index)
	{
		if(index>=0||index<options.length)
			selectIndex=index;
		return selectIndex;
	}
	int selectNextIndex()
	{
		if(selectIndex<options.length-1)
			selectIndex++;;
		return selectIndex;
	}
	int selectPrevIndex()
	{
		if(selectIndex>0)
			selectIndex--;
		return selectIndex;
	}
	int indexOf(CSTR option);
	FlatList();
	BOOL add(CSTR option, int index = -1);
	BOOL rem(CSTR option);
	BOOL rem(int index);
	void clear();
	virtual void copy(const FlatList &flatList);
};

class FlatChoice:public FlatList
{
protected:
	float height;
	int oldZIndex;
	BOOL pressed;
	virtual void draw();
	virtual BOOL input(BYTE type,UINT msg,const Mouse &mouse,const Keyboard &keyboard);
	virtual void setActive(BOOL active)
	{
		FlatComponent::setActive(active);
		if(!active)
		{
			pressed=FALSE;
			if(height)
				rect.height=height;
		}	
	}
public:
	int listColor;
	virtual BOOL resize();
	FlatChoice();
};

class Channel:public Vector<double>
{
friend class FlatPlotter;
protected:
	WORD id;
	int data_index;
	String name;
	FlatPlotter *graph;
	Point2d<float> symPos;
	BOOL symOk;
	Channel *channel_x;
public:
	String arg,sym;
	BOOL readable,hidden;
	GLint stipple_repeat;
	GLushort stipple_pattern;
	DWORD color;
	char symIndex;
	float thick;
	Point2d<float> symOffset;
	double * source;
	Limit<double> limits;

	Channel();
	BOOL findLimits(BOOL toDataIndex);
	int getID() const {return id;}
	CSTR getName() const {return name;}
	BOOL isReadable() const {return readable&&channel_x;}
	BOOL visible() const {return (color&&!hidden&&readable);}
	void setStipple(GLushort pattern = 0xFFFF, GLint repeat = 1)
	{
		stipple_pattern=pattern;
		stipple_repeat=repeat;
	}
	void setSymbol(CSTR symbol, int index = -1, float offsetX = 0.0f, float offsetY = 0.0f)
	{
		sym=symbol;
		symIndex=(char)index;
		symOffset.set(offsetX,offsetY);
	}
	void clear();
	void addValue(const double &value);
	void addValue(){if(source)addValue(*source);}
	double getValue(int index = -1);
	int getDataIndex() const {return data_index;}
	int getSize()const{return channel_x?min(size,channel_x->size):size;}
	BOOL setSource(Vector<double> &newSource);
	BOOL setSource(double *newSource, int newSize);
};

class FlatPlotter:public FlatComponent
{
	friend class FlatGraph;
protected:
	Limit<double> chnLimX,chnLimY;
	WORD channelID;
	Channel *commonX;
	NumberFormat format;
	BOOL processing;
	List<Channel> channels;
	virtual void draw();
	template< class T1, class T2>
	void remapPoint(T1 x, T2 y, float *rx, float *ry)
	{
		if(logscaleX)
			*rx=logremap(x,chnLimX.minValue,chnLimX.maxValue,0.0f,drawRect.width);
		else
			*rx=remap(x,chnLimX.minValue,chnLimX.maxValue,0.0f,drawRect.width);
		if(logscaleY)
			*ry=logremap(y,chnLimY.minValue,chnLimY.maxValue,drawRect.height,0.0f);
		else
			*ry=remap(y,chnLimY.minValue,chnLimY.maxValue,drawRect.height,0.0f);
	}
	void drawVertices(GLenum mode, const double *dataX, const double *dataY, int points, double skipStep, BOOL showLevels = FALSE);
	//List<Point2d<int> *> zooms;
	void setMarked(Channel *channel = NULL)
	{
		marked=channel;
		call(FLAT_ACTION_SELECT,marked);
	}
public:
	typedef List<Channel>::Object ChnlObj;
	Rect<float> drawRect;
	NumberFormat formatX,formatY;
	Channel *marked;
	Font *font;
	CriticalSection cs;
	float
		markLine,
		borderLine,
		gridLine,
		margin;
	BOOL
		showMark,smooth,
		relative,extend,shift,
		skip,fill,
		logscaleX,logscaleY,
		symAuto,symBox,symShow,
		draw_to_index;
	int markIndex;
	DWORD gridColor,markColor,chnlBgColor,fillBlend;
	Limit<double> limX,limY;
	Point2d<float> marginChannel,scale;
	Point2d<int> grid;

	FlatPlotter();
	~FlatPlotter();
	virtual void copy(const FlatPlotter &graph);
	virtual void build();
	virtual BOOL input(	BYTE type,UINT msg,
						const Mouse &mouse,
						const Keyboard &keyboard);

	const List<Channel> * getChannels() const {return &channels;}
	Channel* addChannel(	CSTR name, CSTR arg = NULL,
							DWORD color = 0, int size = 0, double *data = NULL,
							float thickness= 1.0f, BOOL hidden = FALSE,
							BOOL readable = TRUE);
	void resolveNames(BOOL update = FALSE);
	Channel* getChannelByName(CSTR name);
	Channel* getChannelByIndex(int i)
	{
		ListObject<Channel> *channel=channels.objectAt(i);
		if(!channel)
			return NULL;
		return &channel->value;
	}
	Channel* getChannelByPoint(int mouseX, int mouseY, int *index = NULL);
	void remChannels();
	BOOL remChannel(const Channel *channel);
	void update();
	BOOL setXChannel(CSTR xChannelName, CSTR yChannelName = NULL);
	BOOL setSize(int size, CSTR channelName = NULL);
	void stop()
	{
		processing=FALSE;
		cs.Wait();
	}
	void clear(BOOL all = FALSE);
	void addValues();
	void addValues(const double * values);
	void getValues(double * values, int index = -1);
	void hideAll(BOOL hidden = TRUE)
	{
		ListObject<Channel> *channel;
		for(channel=channels.first;channel;channel=channel->next)
			channel->value.hidden=hidden;
	}
	void getRealLims(Limit<double> &limx, Limit<double> &limy) const
	{
		limx.copy(chnLimX);
		limy.copy(chnLimY);
	}
	int load(FILE * file, BOOL rem = TRUE, BOOL text = FALSE, BOOL sampling = FALSE, CSTR term = NULL);
	BOOL save(FILE * file, BOOL readonly = FALSE, CSTR term = NULL);
	DWORD getColor(const Channel *channel) const
	{
		return (channel->color==chnlBgColor)?colorInv(chnlBgColor):channel->color;
	}
};

class FlatGraph:public FlatComponent
{
protected:
	List<Channel*> readChannels;
	String chnl_str;
	int channelStart,channelSpan,_channelRead;
	virtual void draw();
public:
	typedef ListObject<Channel*>* ReadChnlObject;
	int chnlReadLines,chnlBordColor;
	float chnlBordLine,toolmarge;
	BOOL toolbox,chnlinfo,sorted;
	FlatComponent tools;
	FlatScroll scroll;
	FlatText
		tbMinX,tbMaxX,tbMinY,tbMaxY,tbPrecX,tbPrecY,tbGridX,tbGridY,
		tbMarginX,tbMarginY,tbScaleX,tbScaleY,tbColor,tbThick,tbChnls,
		tbStipplePattern,tbStippleRepeat,tbSymbol,tbSymIndex,tbSymOffsetX,tbSymOffsetY;
	FlatButton
		bLog,bSymShow,bSmooth,bSkip,bExp,bLim,bPrec,bGrid,bChnls,
		bMargin,bScale,bSelect,bColor,bThick,bStipple,bSymbol;
	FlatPlotter graph;
	Rect<float> chnlRect;

	FlatGraph();
	virtual BOOL listen(WORD id, WORD code, const void *info);
	virtual void copy(const FlatGraph &graph);
	virtual void build();
	virtual BOOL input(	BYTE type,UINT msg, 
						const Mouse &mouse,
						const Keyboard &keyboard);
	void toggleToolbox()
	{
		toolbox=!toolbox;
		build();
	}
	void showActiveInfo();
};

class FlatSelect:public FlatList
{
protected:
	int optionStart,optionSpan,_readLines;
	virtual void draw();
public:
	FlatScroll scroll;
	int readLines;

	FlatSelect();
	virtual BOOL listen(WORD id, WORD code, const void *info);
	virtual void copy(const FlatSelect &select);
	virtual void build();
	virtual BOOL resize();
	virtual BOOL input(	BYTE type,UINT msg, 
						const Mouse &mouse,
						const Keyboard &keyboard);
};

class FlatImage:public FlatComponent
{
protected:
	BOOL zoom;
	float zoom_x,zoom_y;
	virtual void draw();
	virtual BOOL resize();
public:
	Image *image;
	FlatImage();
	virtual void build();
	virtual void copy(const FlatImage &model);
	virtual BOOL input(BYTE type,UINT msg,const Mouse &mouse,const Keyboard &keyboard);
};

class FlatApplication:public Application
{
protected:
	List<GLObject*> objects;
	int clearColor;
	FileDialog fd;
	Font font;
	FlatComponent container;
	NumberFormat format;
	GLScreen screen;
	CriticalSection draw_sect;

	BOOL add(FlatComponent *component)
	{
		return container.addChild(component);
	}
	BOOL add(FlatComponent *component, int dir, float x = 0, float y = 0,  FlatComponent *reference = NULL)
	{
		return container.addChild(component,dir,x,y,reference);
	}
	BOOL add(GLObject *object)
	{
		return objects.add(object)!=NULL;
	}
	virtual BOOL create();
	virtual BOOL glCreate();
	virtual void glDestroy();
	virtual void reshape ();
	virtual BOOL input(BYTE type);
	virtual void draw();
	BOOL screenshot(
		Image *image,
		GLint x = 0, GLint y = 0,
		GLsizei w = 0, GLsizei h = 0,
		GLenum readbuff = 0,
		GLint align = -1);
	BOOL screenshot(Image *image, const Rect<float> &rect, GLenum readbuff = 0, GLint align = -1, GLint margin = 0)
	{
		return screenshot(	image,
							(UINT)(rect.x+margin),(UINT)(rect.y+margin),
							(UINT)(rect.width-2*margin),(UINT)(rect.height-2*margin),
							readbuff,align);
	}
	virtual void draw2D(){}
	virtual void draw2Dpost(){}
	virtual void draw3D(){}
};

}; // namespace Lio

#endif